package com.xucia.jsponic.data;

import java.lang.ref.Reference;
import java.lang.ref.SoftReference;

import com.xucia.jsponic.datasource.DataSource;
/**
 * An Identification represents an id of object or a value. This allows objects or values to be reference 
 * without the actual object being loaded into the object graph yet. When the object or value
 * is needed, the identification object can retrieve it (by calling getTarget)
 * @author Kris Zyp
 *
 * @param <T> base type of objects that can be referred to
 */
public abstract class Identification<T> {
	
//	TODO: make objectId and source be package protected
	public static Identification<? extends Object> idForString(String value) {
		if (value.indexOf('[') > 0 || value.indexOf('.') > 0) {
			if (JsonPath.isDefinite(value))
				return ObjectPath.idForString(value);
			return JsonPath.idForString(value);
		}
		return ObjectId.idForString(value);
	}
	
	
	public String subObjectId;
	protected Reference<T> targetRef; // this is generally a soft reference, but sometimes it must be a strong/direct reference
	public DataSource source; 
	public boolean isForeign() {
		return source != null && (source.getId().toLowerCase().startsWith("http://") || source.getId().toLowerCase().startsWith("https://"));
	}
	/**
	 * Implementations should implement this to resolve a target when it has not been loaded yet
	 * @return object referred to by identification
	 */
	protected abstract T resolveTarget();
	boolean gettingTarget = false;
	int depth=0;// TODO: This needs to be removed
	/**
	 * Retrieves the value or object referred to by this identification
	 * @return object referred to by identification
	 */	
	public synchronized T getTarget() {
		T finalTarget;
		if (targetRef == null || (finalTarget = targetRef.get()) == null) {
			finalTarget = resolveTarget();
	        targetRef = new SoftReference<T>(finalTarget);
		}
		return finalTarget;
	}
	public String getSubObjectId() {
		return subObjectId;
	}
	public DataSource getSource() {
		return source;
	}
    protected String relativePath(String originalPath, String newPath) {
    	if (newPath.startsWith(originalPath))
    		return newPath.substring(originalPath.length());
    	int originalSlash = originalPath.lastIndexOf('/');
    	if (originalSlash > -1) 
    		return "../" + relativePath(originalPath.substring(0,originalSlash+1), newPath);
    	
    	return newPath; 
    }
    DataSource HTTP_SOURCE =DataSourceManager.getSource("http:"); 
    public String toString(DataSource relativeSource, String relativeSubPath) {
    	if (source == HTTP_SOURCE)
    		return toString();
    	if (source == relativeSource) {
    		if (relativeSubPath == null)
    			return subObjectId;
    		else
    			return relativePath(relativeSubPath, subObjectId);
    	}
		else {
			if (relativeSource == null)
				return toString();
			else
				return "../" + toString();
		}
    }
    @Override
	public String toString() {
    	if (source == null || source.getId().length() == 0)
    		return subObjectId;
    	else
    		return source.getId() + "/" + subObjectId;
    }

}
